package com.tomtop.config;

import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import com.tomtop.core.datasource.config.DruidConfig;
import com.tomtop.core.datasource.config.DruidConfig.DruidNode;
import com.tomtop.core.datasource.config.DruidProperties;
import com.tomtop.core.datasource.core.AbstractDataBase.Mapper;
import com.tomtop.core.datasource.core.AbstractDataBase.Order;
import com.tomtop.core.datasource.core.AbstractDataBaseBean;
import com.tomtop.core.utils.JSONUtils;

@Component
public class DataBaseConfig extends AbstractDataBaseBean implements BeanDefinitionRegistryPostProcessor,EnvironmentAware{
	ConfigurableEnvironment environment;
	static Logger log = LoggerFactory.getLogger(DataBaseConfig.class);

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		DruidConfig druidConfig= this.getDruidConfig();
		DruidProperties defaultConfig = druidConfig.getDefaultConfig();
		Map<String, DruidNode> druidNodeConfigs = druidConfig .getDruid();
		this.setPrimary(druidNodeConfigs);
		System.err.println(JSONUtils.toJSONString(druidConfig));
		druidNodeConfigs.forEach(
				(druidNodeName,druidNodeConfig)->
				this.registryBean(druidNodeName, druidNodeConfig, defaultConfig, registry)
		);
	}
	
	private Map<String, DruidNode> setPrimary(Map<String, DruidNode> druidNodeConfigs) {
		int primarys=0;
		DruidNode defDruidNode = null;
		for (Entry<String, DruidNode> entry : druidNodeConfigs.entrySet()) {
			DruidNode druidNode= entry.getValue();
			if(druidNode!=null&&druidNode.isPrimary()){
				primarys++;
				if(primarys>1)
					druidNode.setPrimary(false);
			}
			if(druidNode!=null&&defDruidNode==null)
				defDruidNode=druidNode;
		}
		if(primarys==0&&defDruidNode!=null)
			defDruidNode.setPrimary(true);
		return druidNodeConfigs;
	}
	
	private DruidConfig getDruidConfig() {
		PropertiesConfigurationFactory<DruidConfig> factory = new PropertiesConfigurationFactory<DruidConfig>(DruidConfig.class);
		factory.setPropertySources(environment.getPropertySources());
		factory.setConversionService(environment.getConversionService());
		factory.setIgnoreInvalidFields(false);
		factory.setIgnoreUnknownFields(true);
		factory.setExceptionIfInvalid(true);
		factory.setIgnoreNestedProperties(false);
		factory.setTargetName("Spring.datasource");
		try {
			factory.bindPropertiesToTarget();
			return factory.getObject();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	private void registryBean(String druidNodeName,DruidNode druidNodeConfig,DruidProperties defaultConfig ,BeanDefinitionRegistry registry) {
		if(druidNodeConfig==null)
			return;
		String mapperPackage=druidNodeConfig.getMapperpackage();
		Order order=druidNodeConfig.getOrder();
		Mapper mappers=druidNodeConfig.getMapper();
		String basepackage=druidNodeConfig.getBasepackage();
		boolean primary=druidNodeConfig.isPrimary();
		
		String dataSourceName=druidNodeName+"DataSource";
		String jdbcTemplateName=druidNodeName+"JdbcTemplate";
		String transactionManagerName=druidNodeName+"TransactionManager";
		String sqlSessionFactoryBeanName=druidNodeName+"SqlSessionFactoryBean";
		String scannerConfigurerName=druidNodeName+"ScannerConfigurer";

		AbstractBeanDefinition dataSource = createDataSource(druidNodeConfig, defaultConfig, dataSourceName);
		AbstractBeanDefinition jdbcTemplate = createJdbcTemplate(dataSourceName);
		AbstractBeanDefinition transactionManager = createTransactionManager(dataSourceName);
		AbstractBeanDefinition sqlSessionFactoryBean = createSqlSessionFactoryBean(dataSourceName, mapperPackage);
		AbstractBeanDefinition scannerConfigurer = createScannerConfigurerBean(sqlSessionFactoryBeanName, basepackage, mappers, order);
		
		dataSource.setPrimary(primary);
		dataSource.setScope(BeanDefinition.SCOPE_SINGLETON);
		jdbcTemplate.setPrimary(primary);
		jdbcTemplate.setScope(BeanDefinition.SCOPE_SINGLETON);
		transactionManager.setPrimary(primary);
		transactionManager.setScope(BeanDefinition.SCOPE_SINGLETON);
		sqlSessionFactoryBean.setPrimary(primary);
		sqlSessionFactoryBean.setScope(BeanDefinition.SCOPE_SINGLETON);
		scannerConfigurer.setPrimary(primary);
		scannerConfigurer.setScope(BeanDefinition.SCOPE_SINGLETON);
		
		registry.registerBeanDefinition(dataSourceName, dataSource);
		registry.registerBeanDefinition(jdbcTemplateName, jdbcTemplate);
		registry.registerBeanDefinition(transactionManagerName, transactionManager);
		registry.registerBeanDefinition(sqlSessionFactoryBeanName, sqlSessionFactoryBean);
		registry.registerBeanDefinition(scannerConfigurerName, scannerConfigurer);
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment=(ConfigurableEnvironment)environment;
	}
}
